﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace Com.SqlSmarts.SSDTPrettySave
{
    public class XmlParser
    {
        private Dictionary<XmlNode, List<NodeAndName>> m_NodeHash;

        private void BuildNodeHashtable(XmlNode Node)
        {
            if (Node.Name == "DTS:Property")
            {
                if (Node.Attributes["DTS:Name"].Value == "ObjectName")
                {
                    if (!this.m_NodeHash.ContainsKey(Node.ParentNode.ParentNode))
                        this.m_NodeHash.Add(Node.ParentNode.ParentNode, new List<NodeAndName>());

                    this.m_NodeHash[Node.ParentNode.ParentNode].Add(new NodeAndName()
                    {
                        m_Node = Node.ParentNode,
                        m_Name = Node.InnerText
                    });
                }
            }

            foreach (XmlNode Node1 in Node.ChildNodes)
                this.BuildNodeHashtable(Node1);
        }

        public void ProcessXml(string filename)
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(filename);
            this.m_NodeHash = new Dictionary<XmlNode, List<NodeAndName>>();
            //this.RemoveNodesForCompare((XmlNode)xmlDocument);
            this.BuildNodeHashtable((XmlNode)xmlDocument);
            Dictionary<XmlNode, List<NodeAndName>>.KeyCollection.Enumerator enumerator1;

            // Remove nodes from the document using the hashtable
            enumerator1 = this.m_NodeHash.Keys.GetEnumerator();
            while (enumerator1.MoveNext())
            {
                XmlNode current1 = enumerator1.Current;
                List<NodeAndName>.Enumerator enumerator2;
                enumerator2 = this.m_NodeHash[current1].GetEnumerator();
                while (enumerator2.MoveNext())
                {
                    NodeAndName current2 = enumerator2.Current;
                    current1.RemoveChild(current2.m_Node);
                }

            }

            // Now add the nodes back in using the hashtable
            Dictionary<XmlNode, List<NodeAndName>>.KeyCollection.Enumerator enumerator3;
            enumerator3 = this.m_NodeHash.Keys.GetEnumerator();
            while (enumerator3.MoveNext())
            {
                XmlNode current1 = enumerator3.Current;
                List<NodeAndName> list = new List<NodeAndName>((IEnumerable<NodeAndName>)this.m_NodeHash[current1]);
                list.Sort();
                List<NodeAndName>.Enumerator enumerator2;

                enumerator2 = list.GetEnumerator();
                while (enumerator2.MoveNext())
                {
                    NodeAndName current2 = enumerator2.Current;
                    current1.AppendChild(current2.m_Node);
                }
            }

            // This setting stops the xml writer from adding a newline and indent to empty nodes when doing an XML pretty print. This is 
            // important as SSIS string varibales set to an empty string are saved as 6 spaces and a new line (without this setting). This
            // can cause problems with logic in the packages.
            XmlWriterSettings settings = new XmlWriterSettings { Indent = true, NewLineOnAttributes = true };
            XmlWriter writer = XmlWriter.Create(filename, settings);
            xmlDocument.PreserveWhitespace = true;
            xmlDocument.Save(writer);
        }

        internal void SaveDocument(string filename)
        {
            try
            {
                if (!IsValidPackage(filename))
                    return;

                BackupDocument(filename);
                ProcessXml(filename);
            }
            catch (Exception ex)
            {
                var message = "The package XML could not be re-ordered and saved:" + Environment.NewLine + ex.Message;
                MessageBox.Show(message, "SsisGit");
            }
        }

        private bool IsValidPackage(string filename)
        {
            var fileExtension = Path.GetExtension(filename);

            switch (fileExtension)
            {
                case ".dtsx":
                    //case ".rdl":
                    //case ".rsd":
                    //case ".rds":
                    return true;
                default:
                    return false;
            }
        }

        private void BackupDocument(string filename)
        {
            File.Copy(filename, filename + ".bak", true);
        }
    }
}
